﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using System.IO;
using SetupLauncher.Domain.Logic;

namespace SetupLauncher.Domain.Configuration.Providers
{
    /// <summary>
    /// Classe que persiste as configurações do SetupLauncher em XML
    /// </summary>
    internal class Xml : IConfiguration
    {
        Logger.ILoggable _log;

        public Xml() {}

        public Xml(Logger.ILoggable log)
        {
            _log = log;
        }

        public List<Entity.Product> Products { get; private set; }
        
        /// <summary>
        /// Adiciona um Produto para ser gravado
        /// </summary>
        /// <param name="product">Produto a ser adicionado na lista</param>
        /// <exception cref="ValidationException"></exception>
        public void AddProduct(Entity.Product product)
        {
            Exceptions.ValidationException exception = new Exceptions.ValidationException(); 
            
            if (this.Products == null)
                this.Products = new List<Entity.Product>();

            if (product == null)
                exception.AddValidation(new Exceptions.Validation
                {
                    Description = "É necessário informa um produto",
                    PropertyName = "product",
                    PropertyTitle = "Produto"
                });

            if (exception.CountValidations > 0)
                throw exception;

            WriteLog("Adicionando o Produto " + product.Name);

            this.Products.Add(product);
        }

        /// <summary>
        /// Salva as informações do Produto
        /// </summary>
        /// <exception cref="SetupLauncher.Domain.Exceptions.ProductListEmptyException"></exception>
        public void Save(string savePath)
        {
            Exceptions.ValidationException exception = new Exceptions.ValidationException(); 

            if(string.IsNullOrEmpty(savePath))
                exception.AddValidation(new Exceptions.Validation
                {
                    Description = "Nenhum Caminho de Arquivo foi Informado",
                    PropertyName = "savePath",
                    PropertyTitle = "Caminho de Arquivo"
                });

            if (this.Products == null || this.Products.Count == 0)
                exception.AddValidation(new Exceptions.Validation
                {
                    Description = "Nenhum Produto foi Informado",
                    PropertyName = "product",
                    PropertyTitle = "Produto"
                });

            if (exception.CountValidations > 0)
                throw exception;

            WriteLog("Salvando as configurações");

            string xmlContent = Serialize();
            System.Xml.XmlDocument document = new System.Xml.XmlDocument();
            document.LoadXml(xmlContent);
            if (System.IO.File.Exists(savePath))
                System.IO.File.Delete(savePath);

            document.Save(savePath);
        }

        /// <summary>
        /// Carrega as Informações
        /// </summary>
        /// <returns>Lista de Produtos</returns>
        public void Load(string savePath)
        {
            Exceptions.ValidationException exception = new Exceptions.ValidationException();

            if (string.IsNullOrEmpty(savePath))
                exception.AddValidation(new Exceptions.Validation
                {
                    Description = "Nenhum Caminho de Arquivo foi Informado",
                    PropertyName = "savePath",
                    PropertyTitle = "Caminho de Arquivo"
                });

            if (!System.IO.File.Exists(savePath))
                exception.AddValidation(new Exceptions.Validation
                {
                    Description = "Caminho de Arquivo inexistente",
                    PropertyName = "savePath",
                    PropertyTitle = "Caminho de Arquivo"
                });

            if (exception.CountValidations > 0)
                throw exception;            
            
            WriteLog("Carregando as configurações");
            
            System.Xml.XmlDocument document = new System.Xml.XmlDocument();
            document.Load(savePath);
            this.Products = null;
            this.Products = Deserialize(document.OuterXml);
            LoadVersions();
        }

        private void LoadVersions()
        {
            TeamSystem ts = new TeamSystem(_log);
            foreach (Entity.Product product in this.Products)
            {
                foreach (Entity.Environment env in product.Environments)
                {
                    List<Entity.Version> versions = null;
                    if (env.LoadAllVersions)
                    {
                        versions = ts.ListFolders(env.TeamSystemPath).ToList();
                    }
                    else
                    {
                        Entity.Version version = ts.GetLastUpdate(env.TeamSystemPath);
                        versions = new List<Entity.Version>();
                        versions.Add(version);
                    }
                    env.Versions = versions;
                }
            }
        }

        public void Erase()
        {
            this.Products = null;
        }

        /// <summary>
        /// Serializa as informações
        /// </summary>
        /// <returns>Conteúdo Xml Serializado</returns>
        private string Serialize()
        {
            WriteLog("Serializando as configurações");

            try
            {
                StringWriter writer = new StringWriter();
                XmlSerializer serializer = new XmlSerializer(this.Products.GetType());

                serializer.Serialize(writer, this.Products);
                return writer.ToString();
            }
            catch (Exception ex)
            {
                WriteException(ex.Message);
                throw ex;
            }
        }

        /// <summary>
        /// Deserializa as informações
        /// </summary>
        /// <param name="xmlContent">Conteúdo Xml Serializado</param>
        /// <returns>Lista de Produtos</returns>
        private List<Entity.Product> Deserialize(string xmlContent)
        {
            WriteLog("Deserializando as configurações");

            try
            {
                StringReader reader = new StringReader(xmlContent);
                XmlSerializer serializer = new XmlSerializer(typeof(List<Entity.Product>));
                return (List<Entity.Product>)serializer.Deserialize(reader);
            }
            catch (Exception ex)
            {
                WriteException(ex.Message);
                throw ex;
            }
        }

        private void WriteLog(string message)
        {
            if (_log == null)
                return;
            _log.Write(message);
        }

        private void WriteException(string message)
        {
            if (_log == null)
                return;
            _log.WriteException(message);
        }
    }
}
